/************************************************************************
 * <br>
 * <ul>
 * <li>版权所有：唐山弘川科技有限公司 2009－2020</li>
 * <li>创建日期：2009-8-10</li>
 * <li>初始作者：刘川</li>
 * </ul>
 * <ul>
 * <b>修订历史</b>
 * <li>1、</li>
 * </ul>
 ***********************************************************************/
package com.hy.enterprise.framework.persistence.domain.query ;

import com.hy.enterprise.framework.persistence.domain.query.internal.AliasFactory;
import com.hy.enterprise.framework.persistence.domain.query.internal.LibraPersistentObjectFactory;
import com.hy.enterprise.framework.persistence.domain.query.internal.managedobjects.LibraManagedObject;

/**
 * <ul>
 * <li>设计作者：刘川</li>
 * <li>设计日期：2009-8-10</li>
 * <li>设计时间：下午03:29:55</li>
 * <li>设计目的：查询中运算的实现类</li>
 * </ul>
 * <ul>
 * <b>修订历史</b>
 * <li>1、</li>
 * </ul>
 */
public class Arithmetics
{
	private static final String	DIVIDE	    =
	                                          " / " ;
	private static final String	MINUS	    =
	                                          " - " ;
	private static final String	PLUS	    =
	                                          " + " ;
	private static final String	TIMES	    =
	                                          " * " ;
	private static final String	UNARY_MINUS	=
	                                          "- " ;
	private static final String	UNARY_PLUS	=
	                                          "+ " ;

	/**
	 * <ul>
	 * <li>设计作者：刘川</li>
	 * <li>设计日期：2009-8-10</li>
	 * <li>设计时间：下午03:33:52</li>
	 * <li>设计目的：构建串联操作</li>
	 * </ul>
	 * <ul>
	 * <b>修订历史</b>
	 * <li>1、</li>
	 * </ul>
	 * 
	 * @param <O>
	 * @param operator
	 * @param operand
	 * @param moreOperands
	 * @return
	 */
	private static < O extends Object > String buildChainedOps (
	                                                             String operator ,
	                                                             O operand ,
	                                                             O ... moreOperands )
	{
		StringBuilder stringBuilder =
		                              new StringBuilder ( ) ;
		stringBuilder
		             .append ( AliasFactory
		                                   .safeStringForm ( operand ) ) ;
		for ( O element : moreOperands )
		{
			stringBuilder
			             .append (
			                       operator )
			             .append (
			                       AliasFactory
			                                   .safeStringForm ( element ) ) ;
		}
		return stringBuilder
		                    .toString ( ) ;
	}

	/**
	 * <ul>
	 * <li>设计作者：刘川</li>
	 * <li>设计日期：2009-8-10</li>
	 * <li>设计时间：下午03:34:34</li>
	 * <li>设计目的：</li>
	 * </ul>
	 * <ul>
	 * <b>修订历史</b>
	 * <li>1、</li>
	 * </ul>
	 * 
	 * @param moreSubQueries
	 * @return
	 */
	private static Number [ ] rememberAllSubQueriesAsNumbers (
	                                                           ISubQuery < ? extends Number > ... moreSubQueries )
	{
		Number [ ] result =
		                    new Number [ moreSubQueries.length ] ;
		for ( int index =
		                  0 ; index < moreSubQueries.length ; index ++ )
		{
			result [ index ] =
			                   Arithmetics
			                              .rememberAsDouble ( moreSubQueries [ index ] ) ;
		}
		return result ;
	}

	private static Double rememberAsDouble (
	                                         final ISubQuery < ? extends Number > subQuery )
	{
		Double subQueryAsNumber =
		                          LibraPersistentObjectFactory.INSTANCE
		                                                               .makeNew ( Double.class ) ;

		AliasFactory
		            .remember (
		                        subQueryAsNumber ,
		                        new LibraManagedObject ( )
		                        {
			                        @ Override
			                        public String getTargetRepresentation ( )
			                        {
				                        return AliasFactory
				                                           .safeStringForm ( subQuery ) ;
			                        }
		                        } ) ;
		return subQueryAsNumber ;
	}

	public static Number divide (
	                              final ISubQuery < ? extends Number > subQueryDividend ,
	                              final ISubQuery < ? extends Number > subQueryDivisor )
	{
		return Arithmetics
		                  .divide (
		                            Arithmetics
		                                       .rememberAsDouble ( subQueryDividend ) ,
		                            Arithmetics
		                                       .rememberAsDouble ( subQueryDivisor ) ) ;
	}

	public static Number divide (
	                              final ISubQuery < ? extends Number > subQueryDividend ,
	                              Number divisor )
	{
		return Arithmetics
		                  .divide (
		                            Arithmetics
		                                       .rememberAsDouble ( subQueryDividend ) ,
		                            divisor ) ;
	}

	public static < N extends Number > N divide (
	                                              final N dividend ,
	                                              final N divisor )
	{
		N quotient =
		             LibraPersistentObjectFactory.INSTANCE
		                                                  .makeNew ( dividend ) ;

		AliasFactory
		            .remember (
		                        quotient ,
		                        new LibraManagedObject ( )
		                        {
			                        @ Override
			                        public String getTargetRepresentation ( )
			                        {

				                        return AliasFactory
				                                           .safeStringForm ( dividend )
				                               + Arithmetics.DIVIDE
				                               + AliasFactory
				                                             .safeStringForm ( divisor ) ;
			                        }
		                        } ) ;

		return quotient ;
	}

	public static Number divide (
	                              Number dividend ,
	                              final ISubQuery < ? extends Number > subQueryDivisor )
	{
		return Arithmetics
		                  .divide (
		                            dividend ,
		                            Arithmetics
		                                       .rememberAsDouble ( subQueryDivisor ) ) ;
	}

	public static Number minus (
	                             final ISubQuery < ? extends Number > subQuery ,
	                             ISubQuery < ? extends Number > ... moreSubQueries )
	{
		Double subQueryAsNumber =
		                          Arithmetics
		                                     .rememberAsDouble ( subQuery ) ;

		if ( moreSubQueries.length == 0 )
		{
			return Arithmetics
			                  .minus ( subQueryAsNumber ) ;
		}

		return Arithmetics
		                  .minus (
		                           subQueryAsNumber ,
		                           Arithmetics
		                                      .rememberAllSubQueriesAsNumbers ( moreSubQueries ) ) ;
	}

	public static Number minus (
	                             final ISubQuery < ? extends Number > subQuery ,
	                             Number operand )
	{
		return Arithmetics
		                  .minus (
		                           Arithmetics
		                                      .rememberAsDouble ( subQuery ) ,
		                           operand ) ;
	}

	public static < N extends Number > N minus (
	                                             final N operand ,
	                                             final N ... moreOperands )
	{
		N difference =
		               LibraPersistentObjectFactory.INSTANCE
		                                                    .makeNew ( operand ) ;

		AliasFactory
		            .remember (
		                        difference ,
		                        new LibraManagedObject ( )
		                        {
			                        @ Override
			                        public String getTargetRepresentation ( )
			                        {

				                        if ( moreOperands.length == 0 )
				                        {
					                        return Arithmetics.UNARY_MINUS
					                               + AliasFactory
					                                             .safeStringForm ( operand ) ;
				                        }

				                        return Arithmetics
				                                          .buildChainedOps (
				                                                             Arithmetics.MINUS ,
				                                                             operand ,
				                                                             moreOperands ) ;
			                        }
		                        } ) ;

		return difference ;
	}

	public static Number minus (
	                             Number operand ,
	                             final ISubQuery < ? extends Number > subQuery )
	{
		return Arithmetics
		                  .minus (
		                           operand ,
		                           Arithmetics
		                                      .rememberAsDouble ( subQuery ) ) ;
	}

	public static < N extends Number > N paren (
	                                             final N arithmeticExpression )
	{
		N grouppedExpression =
		                       LibraPersistentObjectFactory.INSTANCE
		                                                            .makeNew ( arithmeticExpression ) ;

		AliasFactory
		            .remember (
		                        grouppedExpression ,
		                        new LibraManagedObject ( )
		                        {
			                        @ Override
			                        public String getTargetRepresentation ( )
			                        {
				                        return "("
				                               + AliasFactory
				                                             .safeStringForm ( arithmeticExpression )
				                               + ")" ;
			                        }
		                        } ) ;

		return grouppedExpression ;
	}

	public static Number plus (
	                            final ISubQuery < ? extends Number > subQuery ,
	                            final ISubQuery < ? extends Number > ... moreSubQueries )
	{
		Double subQueryAsNumber =
		                          Arithmetics
		                                     .rememberAsDouble ( subQuery ) ;
		if ( moreSubQueries.length == 0 )
		{
			return Arithmetics
			                  .plus ( subQueryAsNumber ) ;
		}

		return Arithmetics
		                  .plus (
		                          subQueryAsNumber ,
		                          Arithmetics
		                                     .rememberAllSubQueriesAsNumbers ( moreSubQueries ) ) ;
	}

	public static Number plus (
	                            final ISubQuery < ? extends Number > subQuery ,
	                            Number operand )
	{
		return Arithmetics
		                  .plus (
		                          Arithmetics
		                                     .rememberAsDouble ( subQuery ) ,
		                          operand ) ;
	}

	public static < N extends Number > N plus (
	                                            final N operand ,
	                                            final N ... moreOperands )
	{
		N sum =
		        LibraPersistentObjectFactory.INSTANCE
		                                             .makeNew ( operand ) ;

		AliasFactory
		            .remember (
		                        sum ,
		                        new LibraManagedObject ( )
		                        {
			                        @ Override
			                        public String getTargetRepresentation ( )
			                        {

				                        if ( moreOperands.length == 0 )
				                        {
					                        return Arithmetics.UNARY_PLUS
					                               + AliasFactory
					                                             .safeStringForm ( operand ) ;
				                        }

				                        return Arithmetics
				                                          .buildChainedOps (
				                                                             Arithmetics.PLUS ,
				                                                             operand ,
				                                                             moreOperands ) ;
			                        }
		                        } ) ;

		return sum ;
	}

	public static Number times (
	                             final ISubQuery < ? extends Number > subQuery1 ,
	                             final ISubQuery < ? extends Number > subQuery2 ,
	                             ISubQuery < ? extends Number > ... moreSubQueries )
	{
		Double subQuery1AsNumber =
		                           Arithmetics
		                                      .rememberAsDouble ( subQuery1 ) ;

		if ( moreSubQueries.length == 0 )
		{
			return Arithmetics
			                  .times (
			                           subQuery1AsNumber ,
			                           Arithmetics
			                                      .rememberAsDouble ( subQuery2 ) ) ;
		}

		return Arithmetics
		                  .times (
		                           subQuery1AsNumber ,
		                           Arithmetics
		                                      .rememberAsDouble ( subQuery2 ) ,
		                           Arithmetics
		                                      .rememberAllSubQueriesAsNumbers ( moreSubQueries ) ) ;
	}

	public static Number times (
	                             final ISubQuery < ? extends Number > subQuery ,
	                             Number factor )
	{
		return Arithmetics
		                  .times (
		                           Arithmetics
		                                      .rememberAsDouble ( subQuery ) ,
		                           factor ) ;
	} ;

	public static < N extends Number > N times (
	                                             final N factor1 ,
	                                             final N factor2 ,
	                                             final N ... moreFactors )
	{
		N product =
		            LibraPersistentObjectFactory.INSTANCE
		                                                 .makeNew ( factor1 ) ;

		AliasFactory
		            .remember (
		                        product ,
		                        new LibraManagedObject ( )
		                        {
			                        @ Override
			                        public String getTargetRepresentation ( )
			                        {

				                        if ( moreFactors.length == 0 )
				                        {
					                        return AliasFactory
					                                           .safeStringForm ( factor1 )
					                               + Arithmetics.TIMES
					                               + AliasFactory
					                                             .safeStringForm ( factor2 ) ;
				                        }

				                        return AliasFactory
				                                           .safeStringForm ( factor1 )
				                               + Arithmetics.TIMES
				                               + Arithmetics
				                                            .buildChainedOps (
				                                                               Arithmetics.TIMES ,
				                                                               factor2 ,
				                                                               moreFactors ) ;
			                        }
		                        } ) ;

		return product ;
	}

	private Arithmetics ( )
	{
	}
}
